<!DOCTYPE html>
<html lang="zh">
<!-- 文档可以参考https://developer.mozilla.org/zh-CN/docs/Web -->
<!-- 浏览器的api可以参考webapi部分 -->



<head>
	<meta charset="UTF-8">
	<title>h264转fmp4</title>
</head>

<body>

	<!-- 一个文件选择框，用来选择文件 -->
	<input type="file" id="fileInput" name="ffrag_bunny.mp4" />

	<!-- 一个画布 -->
	<canvas id="canvas"></canvas>

	<!-- 一个视频播放 -->
	<!-- video标签属性 -->
	<!-- autoplay 尽快播放不等下载完成
	  controls 提供控制面板
	  controlslist 帮助选择控件 -->
	<!-- 调试时开controls可以让你知道播放的情况 -->
	<video id="video1" controls>
		<!-- <source src="h264000060.mp4"> -->
		<!-- 不支持这个标签的浏览器会进到这里 -->
		此浏览器不支持video标签！！！
	</video>



	<script>

		// 注意mp4文件要碎片化
		//var mimeCodec = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';
		var mimeCodec = 'video/mp4; codecs="avc1.64001f, mp4a.40.2"'; //ffrag_bunny.mp4
		//var mimeCodec = 'video/mp4; codecs="avc1.640028"'; //fh264000060.mp4

		var fileInput = document.getElementById("fileInput");
		var video1 = document.getElementById("video1");
		var mediaSource = new MediaSource;
		var reader = new FileReader();

		var types = {
			avc1: [], // codingname
			avcC: [],
			btrt: [],
			dinf: [],
			dref: [],
			esds: [],
			ftyp: [],
			hdlr: [],
			mdat: [],
			mdhd: [],
			mdia: [],
			mfhd: [],
			minf: [],
			moof: [],
			moov: [],
			mp4a: [],
			mvex: [],
			mvhd: [],
			sdtp: [],
			stbl: [],
			stco: [],
			stsc: [],
			stsd: [],
			stsz: [],
			stts: [],
			tfdt: [],
			tfhd: [],
			traf: [],
			trak: [],
			trun: [],
			trex: [],
			tkhd: [],
			vmhd: [],
			smhd: []
		};

		var videoHdlr = new Uint8Array([
			0x00, // version 0
			0x00, 0x00, 0x00, // flags
			0x00, 0x00, 0x00, 0x00, // pre_defined
			0x76, 0x69, 0x64, 0x65, // handler_type: 'vide'
			0x00, 0x00, 0x00, 0x00, // reserved
			0x00, 0x00, 0x00, 0x00, // reserved
			0x00, 0x00, 0x00, 0x00, // reserved
			0x56, 0x69, 0x64, 0x65,
			0x6f, 0x48, 0x61, 0x6e,
			0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'VideoHandler'
		]);

		var audioHdlr = new Uint8Array([
			0x00, // version 0
			0x00, 0x00, 0x00, // flags
			0x00, 0x00, 0x00, 0x00, // pre_defined
			0x73, 0x6f, 0x75, 0x6e, // handler_type: 'soun'
			0x00, 0x00, 0x00, 0x00, // reserved
			0x00, 0x00, 0x00, 0x00, // reserved
			0x00, 0x00, 0x00, 0x00, // reserved
			0x53, 0x6f, 0x75, 0x6e,
			0x64, 0x48, 0x61, 0x6e,
			0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'SoundHandler'
		]);

		var HDLR_TYPES = {
			'video': videoHdlr,
			'audio': audioHdlr
		};

		var dref = new Uint8Array([
			0x00, // version 0
			0x00, 0x00, 0x00, // flags
			0x00, 0x00, 0x00, 0x01, // entry_count
			0x00, 0x00, 0x00, 0x0c, // entry_size
			0x75, 0x72, 0x6c, 0x20, // 'url' type
			0x00, // version 0
			0x00, 0x00, 0x01 // entry_flags
		]);

		var stco = new Uint8Array([
			0x00, // version
			0x00, 0x00, 0x00, // flags
			0x00, 0x00, 0x00, 0x00 // entry_count
		]);
		var STCO = stco;
		var STSC = STCO;
		var STTS = STSC;

		var STSZ = new Uint8Array([
			0x00, // version
			0x00, 0x00, 0x00, // flags
			0x00, 0x00, 0x00, 0x00, // sample_size
			0x00, 0x00, 0x00, 0x00, // sample_count
		]);

		var VMHD = new Uint8Array([
			0x00, // version
			0x00, 0x00, 0x01, // flags
			0x00, 0x00, // graphicsmode
			0x00, 0x00,
			0x00, 0x00,
			0x00, 0x00 // opcolor
		]);

		var SMHD = new Uint8Array([
			0x00, // version
			0x00, 0x00, 0x00, // flags
			0x00, 0x00, // balance
			0x00, 0x00 // reserved
		]);

		var STSD = new Uint8Array([
			0x00, // version 0
			0x00, 0x00, 0x00, // flags
			0x00, 0x00, 0x00, 0x01
		]);// entry_count

		var majorBrand = new Uint8Array([105, 115, 111, 109]); // isom
		var avc1Brand = new Uint8Array([97, 118, 99, 49]); // avc1
		var minorVersion = new Uint8Array([0, 0, 0, 1]);

		var FTYP = null;







		//封装一个box，类型为types里面的元素type
		function box(type) {
			//荷载为arg1
			var payload = Array.prototype.slice.call(arguments, 1);
			//头长度为8 4byte长度，4byte类型
			var size = 8;
			//荷载的长度
			var i = payload.length;
			var len = i;
			//返回的数据
			var result;

			console.log("len", len);

			while (i--) {
				//有几组数据，每组数据的byte长度，全加到size上
				size += payload[i].byteLength;
			}
			//计算出长度以后创建该长度的array
			result = new Uint8Array(size);
			//前4byte长度
			result[0] = (size >> 24) & 0xff;
			result[1] = (size >> 16) & 0xff;
			result[2] = (size >> 8) & 0xff;
			result[3] = size & 0xff;
			//从result的第四个元素开始插type进去
			result.set(type, 4);

			// copy the payload into the result
			for (i = 0, size = 8; i < len; i++) {
				// copy payload[i] array @ offset size
				result.set(payload[i], size);
				size += payload[i].byteLength;
			}
			return result;
		}

		//窗口加载时
		window.addEventListener("load", (e) => {

			var i;
			for (i in types) {
				if (types.hasOwnProperty(i)) {
					//把名字放到各个array里面去
					types[i] = [
						i.charCodeAt(0),
						i.charCodeAt(1),
						i.charCodeAt(2),
						i.charCodeAt(3)
					];
				}
				// console.log(i);
			}
			
			//创建好ftyp box（见mp4box.md中的ftyp box标签）
			FTYP = box(types.ftyp, majorBrand, minorVersion, majorBrand, avc1Brand);
			console.log("FTYP", [FTYP].concat([types.moof]).concat([types.hdlr]));

		})



		fileInput.addEventListener("change", (e) => {
			// 读取文件内容（文本格式）
			var file = e.target.files[0]; // 获取用户选择的文件
			console.log(file);


			reader.readAsArrayBuffer(file);

			//读取完文件以后
			reader.onload = () => {

				var video = document.getElementById("video1");




				if ('MediaSource' in window && MediaSource.isTypeSupported(mimeCodec)) {

					//console.log(mediaSource.readyState); // closed
					video.src = URL.createObjectURL(mediaSource);
					mediaSource.addEventListener('sourceopen', function (_) {
						//
						var sourceBuffer = mediaSource.addSourceBuffer(mimeCodec)
						sourceBuffer.addEventListener('updateend', function (_) {
							console.log('updateend');
							mediaSource.endOfStream();
							video.play();
						})
						console.log('append');
						sourceBuffer.appendBuffer(reader.result);

					});
				} else {
					console.error('Unsupported MIME type or codec: ', mimeCodec);
				}

			};
		});

	</script>

	<style scoped>
		/* 视频和画布都放-1层，操作元素放上面，宽度为100%，高度自适应 */
		#video1,
		#canvas {
			position: fixed;
			left: 0;
			top: 0;
			/* width: 100%; */
			height: 100%;
			/* 可以通过设置z-index来实现漂浮效果 */
			z-index: -1;
		}
	</style>
</body>

</html>